#include <gnome.h>

#include <stdio.h>
#include <ctype.h>

#include "support.h"

#include "preamble.h"

static void
showRegisters (void)
{
  GtkWidget *entry;
  char entry_name[] = "entryGR0", value[] = "0000";
  int i;

  for (i = 0; i <= 4; i++)
    {
      entry_name[7] = i + '0';
      entry = lookup_widget (GTK_WIDGET (appMain), entry_name);

      sprintf (value, "%04X", (UWORD) reg.GR[i]);
      gtk_entry_set_text (GTK_ENTRY (entry), value);
    }

  entry = lookup_widget (GTK_WIDGET (appMain), "entryPC");
  sprintf (value, "%04X", (UWORD) reg.PC);
  gtk_entry_set_text (GTK_ENTRY (entry), value);

  entry = lookup_widget (GTK_WIDGET (appMain), "entryFR");
  value[0] = reg.FR / 2 + '0';
  value[1] = reg.FR % 2 + '0';
  value[2] = '\0';
  gtk_entry_set_text (GTK_ENTRY (entry), value);
}

#if 1
static void
showStack (void)
{
  GtkWidget *clistStack = lookup_widget (GTK_WIDGET (appMain), "clistStack");
  UWORD i;
  gchar *dataStack[3], hexStack[] = "0000", charStack[] = " ";

  gtk_clist_freeze (GTK_CLIST (clistStack));
  gtk_clist_clear (GTK_CLIST (clistStack));
  dataStack[0] = hexStack;
  dataStack[1] = charStack;
  for (i = reg.GR[4]; i != initreg.GR[4]; i++)
    {
      sprintf (hexStack, "%04X", (UWORD) memoryRead (i));
      if (isprint (memoryRead (i)))
	charStack[0] = (char) memoryRead (i);
      else
	charStack[0] = ' ';
      dataStack[2] = g_strdup_printf ("%d", memoryRead (i));
      gtk_clist_append (GTK_CLIST (clistStack), dataStack);
      free (dataStack[2]);
    }
  gtk_clist_columns_autosize (GTK_CLIST (clistStack));
  gtk_clist_thaw (GTK_CLIST (clistStack));
}
#else
static void
showStack (void)
{
  GtkWidget *clistStack = lookup_widget (appMain, "clistStack");
  UWORD i;
  gchar *dataStack[3], addressStack[] = "0000", valueStack[] =
    "0000", charactersStack[] = "a;b";

  if (reg.GR[4] == 0)
    gtk_clist_clear (GTK_CLIST (clistStack));
  else
    {
      gtk_clist_freeze (GTK_CLIST (clistStack));
      gtk_clist_clear (GTK_CLIST (clistStack));

      dataStack[0] = addressStack;
      dataStack[1] = valueStack;
      dataStack[2] = charactersStack;
      for (i = (UWORD) reg.GR[4]; i != (UWORD) initreg.GR[4]; i++)
	{
	  sprintf (addressStack, "%04X", (UWORD) i);
	  sprintf (valueStack, "%04X", (UWORD) memoryRead (i));
	  charactersStack[0] = memoryRead (i) / 0x100;
	  charactersStack[2] = memoryRead (i) % 0x100;
	  if (charactersStack[0] == 0)
	    charactersStack[0] = ' ';
	  gtk_clist_append (GTK_CLIST (clistStack), dataStack);
	}
      gtk_clist_columns_autosize (GTK_CLIST (clistStack));

      gtk_clist_thaw (GTK_CLIST (clistStack));
    }
}
#endif

#if 1
static void
showVariables (void)
{
  GtkWidget *clistVariables = lookup_widget (appMain, "clistVariables");
  VARIABLE *avar;
  gchar *dataVariables[2];

  gtk_clist_freeze (GTK_CLIST (clistVariables));
  gtk_clist_clear (GTK_CLIST (clistVariables));
  for (avar = vars; avar; avar = avar->next)
    {
      dataVariables[0] = avar->name;
      switch (avar->type)
	{
	case VAR_BIN:
	  break;
	case VAR_DEC:
	  dataVariables[1] =
	    g_strdup_printf ("%d", memoryRead (avar->address));
	  break;
	case VAR_HEX:
	  dataVariables[1] =
	    g_strdup_printf ("#%04X", memoryRead (avar->address));
	  break;
	case VAR_LABEL:
	  dataVariables[1] =
	    g_strdup_printf ("#%04X", memoryRead (avar->address));
	  break;
	case VAR_STRING:
	  if ((dataVariables[1] =
	       (char *) malloc (sizeof (char) * (avar->length + 1))) != NULL)
	    {
	      int i;

	      for (i = 0; i < avar->length; i++)
		dataVariables[1][i] = (char) memoryRead (avar->address + i);
	      dataVariables[1][avar->length] = '\0';
	    }
	  else
	    {
	      dataVariables[1] = NULL;
	      display (_("No enought memory.\n"));
	    }
	  break;
	}
      gtk_clist_prepend (GTK_CLIST (clistVariables), dataVariables);
      if (dataVariables[1] != NULL)
	free (dataVariables[1]);
    }
  gtk_clist_columns_autosize (GTK_CLIST (clistVariables));
  gtk_clist_thaw (GTK_CLIST (clistVariables));
}
#else
static void
showVariables (void)
{
  GtkWidget *clistVariables = lookup_widget (appMain, "clistVariables");
  VARIABLE *avar;
  gint i;
  gchar *dataVariables[3], *valueVariables, *charactersVariables;

  gtk_clist_freeze (GTK_CLIST (clistVariables));
  gtk_clist_clear (GTK_CLIST (clistVariables));

  for (avar = vars; avar; avar = avar->next)
    {
      dataVariables[0] = avar->name;
      valueVariables = g_malloc (avar->length * 5);
      charactersVariables = g_malloc (avar->length + 1);
      for (i = 0; i < avar->length; i++)
	{
	  sprintf (&valueVariables[i * 5], "%04X",
		   (UWORD) memoryRead (avar->address + i));
	  valueVariables[i * 5 + 4] = ' ';
	  charactersVariables[i] =
	    (UWORD) memoryRead ((avar->address + i) % 0x100);
	}
      valueVariables[avar->length * 5 - 1] = '\0';
      charactersVariables[avar->length] = '\0';
      dataVariables[1] = valueVariables;
      dataVariables[2] = charactersVariables;
      gtk_clist_prepend (GTK_CLIST (clistVariables), dataVariables);
      g_free (valueVariables);
      g_free (charactersVariables);
    }
  gtk_clist_columns_autosize (GTK_CLIST (clistVariables));

  gtk_clist_thaw (GTK_CLIST (clistVariables));
}
#endif

static gchar *
unassembleE (gchar * instructionProgram, int ADR, int XRno)
{
  gchar *stringE, *stringInstruction = instructionProgram;

  if (XRno)
    stringE = g_strdup_printf ("%d,GR%d", ADR, XRno);
  else
    stringE = g_strdup_printf ("%d", ADR);

  instructionProgram = g_strconcat (stringInstruction, stringE, NULL);

  g_free (stringInstruction);
  g_free (stringE);

  return instructionProgram;
}

static gchar *
Dump (int startAddr, int endAddr)
{
  gchar *dumpProgram, *stringProgram, *stringDump;
  gint i;

  dumpProgram = g_strdup_printf ("%04X ", (UWORD) memoryRead (startAddr));
  for (i = startAddr + 1; i < endAddr; i++)
    {
      stringProgram = dumpProgram;
      stringDump = g_strdup_printf ("%04X", (UWORD) memoryRead (i));

      dumpProgram = g_strjoin (" ", stringProgram, stringDump, NULL);

      g_free (stringProgram);
      g_free (stringDump);
    }

  return dumpProgram;
}

static void
showProgram (void)
{
  GtkWidget *clistProgram = lookup_widget (appMain, "clistProgram");
  gchar *dataProgram[3], addressProgram[] = "0000", *instructionProgram =
    NULL, *dumpProgram;

  CODE code;
  UWORD address, GRno, XRno, ADR, op;
  int i, j, k = 0;

  gtk_clist_freeze (GTK_CLIST (clistProgram));
  gtk_clist_clear (GTK_CLIST (clistProgram));
  for (i = 0; i < SentenceNum; i++)
    if (sentences[i].length != 0)	/* the length of instruction is more than 0, it means the instruction cat be execute */
      {
	address = sentences[i].address;
	if (sentences[i].type == MACHINE)
	  for (j = 0; j < sentences[i].length; j += 2)
	    {
	      CodeRead (address + j, &code);
	      ADR = code.ADR;
	      op = code.op;
	      GRno = code.GRno;
	      XRno = code.XRno;

	      switch (mot[op].type)
		{
		case GR_E:	/* GR,E */
		  instructionProgram =
		    g_strdup_printf ("%-5s GR%d,", mot[op].name, GRno);
		  dataProgram[1] =
		    unassembleE (instructionProgram, ADR, XRno);
		  break;
		case GR:	/* GR */
		  dataProgram[1] =
		    g_strdup_printf ("%-5s GR%d", mot[op].name, GRno);
		  break;
		case E:	/* E */
		  instructionProgram =
		    g_strdup_printf ("%-5s ", mot[op].name);
		  dataProgram[1] =
		    unassembleE (instructionProgram, ADR, XRno);
		  break;
		case BLANK:	/* BLANK */
		case ALABEL_NLABEL:	/* IN or OUT ALABEL,NLABEL */
		  dataProgram[1] = g_strdup_printf ("%-5s", mot[op].name);
		  break;
		}

	      sprintf (addressProgram, "%04X", (unsigned int) address + j);
	      dumpProgram = Dump (address + j, address + j + 2);
	      dataProgram[0] = addressProgram;
	      dataProgram[2] = dumpProgram;
	      k = gtk_clist_append (GTK_CLIST (clistProgram), dataProgram);

	      g_free (dataProgram[1]);
	      g_free (dataProgram[2]);
	    }
	else			/* DC or DS */
	  for (j = 0; j < sentences[i].length; j++)
	    {
	      sprintf (addressProgram, "%04X", (unsigned int) address + j);
	      dataProgram[0] = addressProgram;
	      if (isprint (memoryRead (address + j)))
		dataProgram[1] =
		  g_strdup_printf ("%-5s %5d ;%c", "DC",
				   (UWORD) memoryRead (address + j),
				   (char) memoryRead (address + j));
	      else
		dataProgram[1] =
		  g_strdup_printf ("%-5s %5d", "DC",
				   (UWORD) memoryRead (address + j));
	      dataProgram[2] = Dump (address + j, address + j + 1);
	      k = gtk_clist_append (GTK_CLIST (clistProgram), dataProgram);

	      g_free (dataProgram[1]);
	      g_free (dataProgram[2]);
	    }
	/* select current program line */
	if (sentences[i].address == reg.PC)
	  gtk_clist_select_row (GTK_CLIST (clistProgram), k, 0);
      }
  gtk_clist_columns_autosize (GTK_CLIST (clistProgram));
  gtk_clist_thaw (GTK_CLIST (clistProgram));
}

/* show memory just after malloc
 *
 */
void
showMemory (void)
{
  GtkWidget *clistMemory = lookup_widget (appMain, "clistMemory");
  const gint size = WORD_PER_LINE;
  gchar *dataMemory[3], addressMemory[] =
    "0000", dumpMemory[size * 5], charactersMemory[size + 1];
  gint i, j;

  gtk_clist_freeze (GTK_CLIST (clistMemory));
  gtk_clist_clear (GTK_CLIST (clistMemory));

  dataMemory[0] = addressMemory;
  dataMemory[1] = dumpMemory;
  dataMemory[2] = charactersMemory;
  for (i = 0; i < MEMORY_SIZE; i += size)
    {
      sprintf (addressMemory, "%04X", (unsigned int) i);
      for (j = 0; j < size; j++)
	{
	  sprintf (dumpMemory + j * 5, "%04X", (UWORD) memoryRead (i + j));
	  dumpMemory[(j + 1) * 5 - 1] = ' ';
	  if (isprint ((char) memoryRead (i + j)))
	    charactersMemory[j] = (UWORD) memoryRead (i + j) % 0x100;
	  else
	    charactersMemory[j] = '.';
	}
      dumpMemory[size * 5 - 1] = '\0';
      charactersMemory[size] = '\0';
      gtk_clist_append (GTK_CLIST (clistMemory), dataMemory);
    }
  gtk_clist_columns_autosize (GTK_CLIST (clistMemory));
  gtk_clist_thaw (GTK_CLIST (clistMemory));
}

static void
reshowMemory (void)
{
  GtkWidget *clistMemory =
    lookup_widget (GTK_WIDGET (appMain), "clistMemory");
  gchar addressMemory[] =
    "0000", dumpMemory[WORD_PER_LINE * sizeof (addressMemory)],
    charactersMemory[WORD_PER_LINE + 1];
  DWORD address, line, offset;

  gtk_clist_freeze (GTK_CLIST (clistMemory));

  for (address = 0; address < MEMORY_SIZE;
       address += WORD_PER_LINE * sizeof (char) * 8)
    if (GetModified (address) == TRUE)
      {
	for (line = 0; line < WORD_PER_LINE * sizeof (char) * 8;
	     line += WORD_PER_LINE)
	  {
	    sprintf (addressMemory, "%04X", address + line);	/* fill address */

	    for (offset = 0; offset < WORD_PER_LINE; offset++)
	      {
		sprintf (dumpMemory + offset * sizeof (addressMemory), "%04X",
			 memoryRead (address + line + offset));
		dumpMemory[(offset + 1) * sizeof (addressMemory) - 1] = ' ';
		if (isprint (memoryRead (address + line + offset)))
		  charactersMemory[offset] =
		    (UWORD) memoryRead (address + line + offset);
		else
		  charactersMemory[offset] = '.';
	      }
	    dumpMemory[WORD_PER_LINE * sizeof (addressMemory) - 1] = '\0';
	    charactersMemory[WORD_PER_LINE] = '\0';

	    gtk_clist_set_text (GTK_CLIST (clistMemory),
				(address + line) / WORD_PER_LINE, 0,
				addressMemory);
	    gtk_clist_set_text (GTK_CLIST (clistMemory),
				(address + line) / WORD_PER_LINE, 1,
				dumpMemory);
	    gtk_clist_set_text (GTK_CLIST (clistMemory),
				(address + line) / WORD_PER_LINE, 2,
				charactersMemory);
	  }
	ClearModified (address);
      }
  gtk_clist_columns_autosize (GTK_CLIST (clistMemory));
  gtk_clist_thaw (GTK_CLIST (clistMemory));
}

void
showState (void)		/* show state */
{
  showProgram ();		/* show program again, maybe some in program changed in running */
  showStack ();			/* show stack again */
  showRegisters ();		/* show registers again */
  showVariables ();		/* show variables again */
  reshowMemory ();		/* show memory again */
}

void show(void)
{
  while (reg.PC<end_PC)
    showState();
}
